#pragma once
#include "base/WaitableEvent.h"
#include "MessageLoop.h"

namespace xBase
{
    class CxThread
    {
    public:

        struct CxOptions 
        {
        public:
            // Specifies the type of message loop that will be allocated on the thread.
            LOOP_TYPE m_eMessageLoopType;

            // Specifies the maximum stack size that the thread is allowed to use.
            // This does not necessarily correspond to the thread's initial stack size.
            // A value of 0 indicates that the default maximum should be used.
            UINT m_nStackSize;

            BOOL m_bUseCom;

            DWORD m_dwCoInitExFlag;

            LPCSTR m_pszThreadName;

            CxOptions()
                : m_eMessageLoopType(LOOP_TYPE_DEFAULT)
                , m_nStackSize(0)
                , m_bUseCom(FALSE)
                , m_dwCoInitExFlag((DWORD)-1)
                , m_pszThreadName(NULL)
            {}

            CxOptions(LOOP_TYPE eType, UINT nSize, BOOL bUseCom = FALSE, DWORD dwCoInitExFlag = -1, LPCSTR pszThreadName = NULL)
                : m_eMessageLoopType(eType)
                , m_nStackSize(nSize)
                , m_bUseCom(bUseCom)
                , m_dwCoInitExFlag(dwCoInitExFlag)
                , m_pszThreadName(pszThreadName)
            {}
        };

        struct CxStartupData
        {
        public:
            // We get away with a const reference here because of how we are allocated.
            const CxOptions& m_ThreadOptions;

            // Used to synchronize thread startup.
            CxWaitableEvent m_StartedEvent;

            explicit CxStartupData(const CxOptions& opt)
                : m_ThreadOptions(opt)
                , m_StartedEvent(false, false)
            {}
        };

    public:
        explicit CxThread(LPCSTR pszDefaultName = NULL);
        virtual ~CxThread();

    public:
        // StartWithOptions(COptions())
        BOOL Start();

        // begin thread and wait for start event.
        BOOL StartWithOptions(const CxOptions& Options);

        // The thread should only be called on the same thread that started us.
        // invoke StopSoon, wait thread to exit
        BOOL Stop();

        // post CThreadQuitTask task
        BOOL StopSoon();

        // member access function
        CxMessageLoop * GetMessageLoop() const { return m_pThreadMessageLoop; }

        BOOL GetMessageLoopProxy(CScopedRefPtr<CxMessageLoopProxy>& msgLoopProxy)
        {
            if(m_msgLoopProxy)
            {
                msgLoopProxy = m_msgLoopProxy;
                return TRUE;
            }
            return FALSE;
        }

        HANDLE GetThreadHandle() { return m_hThread; }

        DWORD GetThreadID() const { return m_dwThreadID; }

        BOOL IsRunning() const { return m_dwThreadID != 0; }

        // thread function
        VOID ThreadMain();

        LOOP_TYPE GetType();

    protected:
        virtual VOID Run(CxMessageLoop * pMessageLoop);

        VOID SetMessageLoop(CxMessageLoop * pMessageLoop)
        {
            m_pThreadMessageLoop = pMessageLoop;
        }

    private:

        // We piggy-back on the startup_data_ member to know if we successfully
        // started the thread.  This way we know that we need to call Join.
        BOOL ThreadWasStarted() const { return NULL != m_pStartupData; }

    private:
        // If true, we're in the middle of stopping, and shouldn't access
        // |message_loop_|. It may non-NULL and invalid.
        BOOL m_bStopping;

        CxStartupData * m_pStartupData;

        // The thread's message loop.  Valid only while the thread is alive.
        CxMessageLoop * m_pThreadMessageLoop;

        CScopedRefPtr<CxMessageLoopProxy> m_msgLoopProxy;

        DWORD   m_dwThreadID;
        HANDLE  m_hThread;

        char    m_szName[64];
    };
}